//! 内存读写接口管理

use objectid::ObjectId;

/// 内存事务属性
///
/// 每个内存事务都与一组属性关联. 其中一些是通用的(例如总线主机 ID);
/// 有些是特定于特定类型的总线(如 ARM 安全/非安全位). 将它们都定义为单个结构体中的非重叠位域,
/// 以避免在 Simink 的不同部分使用相同的位来表示不同的语义时产生的混淆
#[derive(Debug, Default, Clone, Copy)]
pub struct MemTxAttr {
    /// 没有指定任何属性的总线主机将得到这个, 这样就可以在必要时区分"所有属性故意清除"和"未指定"
    pub unspecified: bool,
    /// * ARM/AMBA: TrustZone 安全访问
    /// * x86: 系统管理模式访问
    pub secure: bool,
    /// 用户模式内存访问(非特权)
    pub user: bool,
    /// 默认情况下, 总线互连和外设可以访问任何东西(存储器, 设备).
    /// 通过设置 memory, 总线事务与设备相比被限制为"正常"内存. 对设备的访问将被记录并被拒绝(MemTxAccessError)
    pub memory: bool,
    /// 请求 ID (for MSI for example)
    pub requester_id: u16,
    /// 该页的反向字节序
    pub byte_swap: bool,
    /// 下面是特定于 target 的页表 bit. 这些与实际的内存事务完全无关. 然而,
    /// 这个结构是 tlb_fill 接口的一部分, 缓存在 cputlb 结构中, 并且有未使用的 bit.
    /// 这些字段将有特定于 target 的 helper 程序使用.
    pub target_tlb_bit0: bool,
    /// 同上
    pub target_tlb_bit1: bool,
    /// 同上
    pub target_tlb_bit2: bool,
}

impl MemTxAttr {
    /// 默认的内存事务属性
    pub fn memtxattrs_unspecified() -> Self {
        Self { unspecified: true, ..Default::default() }
    }
}

/// 内存事务结果
///
/// 功能更多的 MMIO 访问器, 可以指示事务失败.
/// `MemTxNone` 响应表示未实现, `MemTxOk` 响应表示成功, 其他任何事情都是某种失败.
/// 如果内存子系统从多个较小的访问中合成一个操作, 它将按照位或组合结果.
pub enum MemTxResult {
    /// 未实现
    MemTxNone  = 0,
    /// 成功
    MemTxOk    = 1 << 0,
    /// 设备返回错误
    MemTxError = 1 << 1,
    /// 那个地址什么都没有
    MemTxDecodeError = 1 << 2,
    /// 拒绝访问
    MemTxAccessError = 1 << 3,
}

/// 设备的内存事务读写字节序
pub enum DeviceEndian {
    /// 依照本地字节序读写
    NativeEndian,
    /// 按照大端格式读写
    BigEndian,
    /// 按照小端格式读写
    LittleEndian,
}

/// 内存域操作接口实现
///
/// 并不是所有内存域都需要去实现该接口, 比如 ram 内存可以快速的读写, 不需要去实现
#[allow(unused_variables)]
pub trait MemoryRegionImpl: ObjectId {
    /// 从内存域读
    fn read(&self, offset: u64, size: usize) -> u64;
    /// 向内存域写
    fn write(&self, offset: u64, data: u64, size: usize);
    /// 设备字节序
    fn endianness(&self) -> DeviceEndian;
    /// 设备内存域大小
    fn region_size(&self) -> usize;

    /// 读事务
    fn read_with_attrs(
        &self,
        offset: u64,
        data: &mut u64,
        size: usize,
        attrs: MemTxAttr,
    ) -> MemTxResult {
        MemTxResult::MemTxNone
    }
    /// 写事务
    fn write_with_attrs(
        &self,
        offset: u64,
        data: u64,
        size: usize,
        attrs: MemTxAttr,
    ) -> MemTxResult {
        MemTxResult::MemTxNone
    }

    /// 客户机访问约束
    ///
    /// 如果不为 0, 则指定访问大小的界限, 超出该界限将引发机器检查
    fn valid_min_access_size(&self) -> usize {
        0
    }
    /// 客户机访问约束
    ///
    /// 如果不为 0, 则指定访问大小的界限, 超出该界限将引发机器检查
    fn valid_max_access_size(&self) -> usize {
        0
    }
    /// 客户机访问约束
    ///
    /// 如果为 true, 则支持未对齐访问, 否则未对齐的访问将引发机器检查
    fn valid_unaligned(&self) -> bool {
        false
    }

    /// 客户机访问约束
    ///
    /// 如果实现它, 并返回 false, 则事务不被设备接受(并引导机器相关行为, 如机器检查异常).
    fn valid_accepts(&self, offset: u64, size: usize, is_write: bool, attrs: MemTxAttr) -> bool {
        true
    }

    /// 内部实现约束
    ///
    /// 如果不为 0, 则指定实现的最小大小, 较小的大小将向上四舍五入, 并返回部分结果
    fn impl_min_access_size(&self) -> usize {
        0
    }
    /// 内部实现约束
    ///
    /// 如果不为 0, 则指定实现的最大大小, 较大的大小将通过一系列具有较小大小的访问来完成
    fn impl_max_access_size(&self) -> usize {
        0
    }
    /// 内部实现约束
    ///
    /// 如果为 true, 则支持未对齐访问, 否则所有访问都转换为(可能是多个)自然对齐的访问
    fn impl_unaligned(&self) -> bool {
        false
    }
}
